#!/usr/bin/env node

const fs = require("fs");
const path = require("path");
const { program } = require("commander");
const sharp = require("sharp");
const glob = require("glob");
const chalk = require("chalk");
const cliProgress = require("cli-progress");

// 支持的图片格式
const SUPPORTED_FORMATS = [".jpg", ".jpeg", ".png", ".webp"];

// 默认配置
const DEFAULT_CONFIG = {
  quality: 80,
  width: null,
  height: null,
  scale: null,
  concurrent: 5,
  skipCompressed: true,
  preserveExif: false,
  overwrite: false,
  compressByDir: false,
};

class ImageCompressor {
  constructor(options = {}) {
    this.config = { ...DEFAULT_CONFIG, ...options };
    this.stats = {
      total: 0,
      processed: 0,
      skipped: 0,
      errors: 0,
      originalSize: 0,
      compressedSize: 0,
    };
  }

  // 获取所有图片文件
  async getImageFiles(inputDir) {
    const patterns = SUPPORTED_FORMATS.map((ext) =>
      path.join(inputDir, "**", `*${ext}`).replace(/\\/g, "/")
    );

    let files = [];
    for (const pattern of patterns) {
      const matches = await glob.glob(pattern, { nocase: true });
      files = files.concat(matches);
    }

    return files.map((file) => path.resolve(file));
  }

  // 检查文件是否已压缩
  isCompressed(filePath) {
    if (!this.config.skipCompressed) return false;

    const basename = path.basename(filePath, path.extname(filePath));
    return basename.includes("_compressed") || basename.includes("_comp");
  }

  // 生成输出文件路径
  getOutputPath(inputPath, outputDir, inputRootDir = null) {
    if (this.config.overwrite) {
      return inputPath;
    }

    if (outputDir) {
      // 如果启用按目录压缩模式且有输入根目录
      if (this.config.compressByDir && inputRootDir) {
        const relativePath = path.relative(inputRootDir, inputPath);
        return path.join(outputDir, relativePath);
      }
      const relativePath = path.relative(path.dirname(inputPath), inputPath);
      return path.join(outputDir, relativePath);
    }

    const dir = path.dirname(inputPath);
    const name = path.basename(inputPath, path.extname(inputPath));
    const ext = path.extname(inputPath);

    // 如果启用按目录压缩模式
    if (this.config.compressByDir) {
      const dirName = path.basename(dir);
      const compressedDirName = `${dirName}_compressed`;
      const parentDir = path.dirname(dir);
      const compressedDir = path.join(parentDir, compressedDirName);
      return path.join(compressedDir, `${name}${ext}`);
    }

    return path.join(dir, `${name}_compressed${ext}`);
  }

  // 压缩单个图片
  async compressImage(inputPath, outputPath) {
    try {
      // 获取原始文件大小
      const originalStats = await fs.promises.stat(inputPath);
      this.stats.originalSize += originalStats.size;

      // 确保输出目录存在
      const outputDir = path.dirname(outputPath);
      await fs.promises.mkdir(outputDir, { recursive: true });

      // 创建sharp实例
      let sharpInstance = sharp(inputPath);

      // 获取图片元数据
      const metadata = await sharpInstance.metadata();

      // 设置压缩选项
      const options = { quality: this.config.quality };

      // 处理尺寸调整
      if (this.config.width || this.config.height) {
        sharpInstance = sharpInstance.resize(
          this.config.width,
          this.config.height,
          {
            fit: "inside",
            withoutEnlargement: true,
          }
        );
      } else if (this.config.scale && this.config.scale !== 100) {
        const newWidth = Math.round((metadata.width * this.config.scale) / 100);
        const newHeight = Math.round(
          (metadata.height * this.config.scale) / 100
        );
        sharpInstance = sharpInstance.resize(newWidth, newHeight);
      }

      // 保留EXIF数据
      if (this.config.preserveExif) {
        sharpInstance = sharpInstance.withMetadata();
      }

      // 根据格式设置压缩参数
      const ext = path.extname(inputPath).toLowerCase();
      switch (ext) {
        case ".jpg":
        case ".jpeg":
          await sharpInstance.jpeg(options).toFile(outputPath);
          break;
        case ".png":
          await sharpInstance
            .png({
              quality: this.config.quality,
              compressionLevel: 9,
            })
            .toFile(outputPath);
          break;
        case ".webp":
          await sharpInstance.webp(options).toFile(outputPath);
          break;
        default:
          throw new Error(`不支持的格式: ${ext}`);
      }

      // 获取压缩后文件大小
      const compressedStats = await fs.promises.stat(outputPath);
      this.stats.compressedSize += compressedStats.size;
      this.stats.processed++;

      const compressionRatio = (
        ((originalStats.size - compressedStats.size) / originalStats.size) *
        100
      ).toFixed(1);

      return {
        success: true,
        originalSize: originalStats.size,
        compressedSize: compressedStats.size,
        compressionRatio: parseFloat(compressionRatio),
      };
    } catch (error) {
      this.stats.errors++;
      return {
        success: false,
        error: error.message,
      };
    }
  }

  // 格式化文件大小
  formatFileSize(bytes) {
    const units = ["B", "KB", "MB", "GB"];
    let size = bytes;
    let unitIndex = 0;

    while (size >= 1024 && unitIndex < units.length - 1) {
      size /= 1024;
      unitIndex++;
    }

    return `${size.toFixed(2)} ${units[unitIndex]}`;
  }

  // 批量压缩
  async compress(inputDir, outputDir = null) {
    console.log(chalk.blue("🚀 开始批量压缩图片..."));
    console.log(chalk.gray(`输入目录: ${inputDir}`));
    if (outputDir) {
      console.log(chalk.gray(`输出目录: ${outputDir}`));
    }
    console.log(chalk.gray(`压缩质量: ${this.config.quality}%`));
    console.log(chalk.gray(`并发数: ${this.config.concurrent}`));
    console.log("");

    // 获取所有图片文件
    const imageFiles = await this.getImageFiles(inputDir);

    if (imageFiles.length === 0) {
      console.log(chalk.yellow("⚠️  未找到支持的图片文件"));
      return;
    }

    // 过滤已压缩的文件
    const filesToProcess = imageFiles.filter(
      (file) => !this.isCompressed(file)
    );

    if (filesToProcess.length === 0) {
      console.log(chalk.yellow("⚠️  所有文件都已压缩，跳过处理"));
      return;
    }

    this.stats.total = filesToProcess.length;
    this.stats.skipped = imageFiles.length - filesToProcess.length;

    console.log(
      chalk.green(
        `📁 找到 ${imageFiles.length} 个图片文件，将处理 ${filesToProcess.length} 个`
      )
    );
    if (this.stats.skipped > 0) {
      console.log(chalk.yellow(`⏭️  跳过 ${this.stats.skipped} 个已压缩文件`));
    }
    console.log("");

    // 创建进度条
    const progressBar = new cliProgress.SingleBar({
      format:
        "压缩进度 |" +
        chalk.cyan("{bar}") +
        "| {percentage}% | {value}/{total} | 速度: {speed} 文件/秒 | ETA: {eta}s",
      barCompleteChar: "█",
      barIncompleteChar: "░",
      hideCursor: true,
    });

    progressBar.start(filesToProcess.length, 0, { speed: 0 });

    // 并发处理
    const startTime = Date.now();
    const semaphore = new Array(this.config.concurrent).fill(null);
    let fileIndex = 0;

    const processNext = async () => {
      if (fileIndex >= filesToProcess.length) return;

      const currentIndex = fileIndex++;
      const inputPath = filesToProcess[currentIndex];
      const outputPath = this.getOutputPath(inputPath, outputDir, inputDir);

      const result = await this.compressImage(inputPath, outputPath);

      const processed = this.stats.processed + this.stats.errors;
      const elapsed = (Date.now() - startTime) / 1000;
      const speed = (processed / elapsed).toFixed(1);

      progressBar.update(processed, { speed });

      if (currentIndex < filesToProcess.length - 1) {
        await processNext();
      }
    };

    // 启动并发处理
    await Promise.all(semaphore.map(() => processNext()));

    progressBar.stop();

    // 显示统计结果
    this.showStats();
  }

  // 显示统计信息
  showStats() {
    console.log("");
    console.log(chalk.green("✅ 压缩完成！"));
    console.log("");
    console.log(chalk.blue("📊 统计信息:"));
    console.log(`   总文件数: ${this.stats.total}`);
    console.log(`   成功处理: ${chalk.green(this.stats.processed)}`);
    console.log(`   跳过文件: ${chalk.yellow(this.stats.skipped)}`);
    console.log(`   错误文件: ${chalk.red(this.stats.errors)}`);

    if (this.stats.processed > 0) {
      const totalSaved = this.stats.originalSize - this.stats.compressedSize;
      const totalRatio = ((totalSaved / this.stats.originalSize) * 100).toFixed(
        1
      );

      console.log("");
      console.log(chalk.blue("💾 压缩效果:"));
      console.log(
        `   原始大小: ${this.formatFileSize(this.stats.originalSize)}`
      );
      console.log(
        `   压缩后大小: ${this.formatFileSize(this.stats.compressedSize)}`
      );
      console.log(
        `   节省空间: ${chalk.green(
          this.formatFileSize(totalSaved)
        )} (${totalRatio}%)`
      );
    }
  }
}

// 命令行接口
program
  .name("compress-images")
  .description("批量压缩图片工具")
  .version("1.0.0");

program
  .argument("<input>", "输入目录路径")
  .option("-o, --output <dir>", "输出目录路径")
  .option("-q, --quality <number>", "压缩质量 (1-100)", "80")
  .option("-w, --width <number>", "目标宽度")
  .option("-h, --height <number>", "目标高度")
  .option("-s, --scale <number>", "缩放百分比")
  .option("-c, --concurrent <number>", "并发处理数", "5")
  .option("--overwrite", "覆盖原文件", false)
  .option("--no-skip-compressed", "不跳过已压缩文件", false)
  .option("--preserve-exif", "保留EXIF元数据", false)
  .option("--compress-by-dir", "按目录名称压缩，保持目录结构", false)
  .action(async (input, options) => {
    try {
      // 验证输入目录
      if (!fs.existsSync(input)) {
        console.error(chalk.red("❌ 输入目录不存在"));
        process.exit(1);
      }

      // 解析选项
      const config = {
        quality: parseInt(options.quality),
        width: options.width ? parseInt(options.width) : null,
        height: options.height ? parseInt(options.height) : null,
        scale: options.scale ? parseInt(options.scale) : null,
        concurrent: parseInt(options.concurrent),
        overwrite: options.overwrite,
        skipCompressed: options.skipCompressed,
        preserveExif: options.preserveExif,
        compressByDir: options.compressByDir,
      };

      // 验证参数
      if (config.quality < 1 || config.quality > 100) {
        console.error(chalk.red("❌ 压缩质量必须在 1-100 之间"));
        process.exit(1);
      }

      if (config.concurrent < 1 || config.concurrent > 20) {
        console.error(chalk.red("❌ 并发数必须在 1-20 之间"));
        process.exit(1);
      }

      // 创建压缩器实例
      const compressor = new ImageCompressor(config);

      // 开始压缩
      await compressor.compress(input, options.output);
    } catch (error) {
      console.error(chalk.red("❌ 发生错误:"), error.message);
      process.exit(1);
    }
  });

// 如果直接运行此文件
if (require.main === module) {
  program.parse();
}

module.exports = ImageCompressor;
